package gnss

import (
	"dgo/goutils/dnet"
	"dgo/goutils/utils"
	"encoding/base64"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"time"
)

type EventNtripClientBuffer func(sender *NtripClient, buffer []byte)
type EventNtripClient func(sender *NtripClient)

var (
	ntrip_user_agents = []string{"CHC LandStar/7.3.1.20181130", "GNSSInternetRadio/1.4.11 115.3346 35.7263", "ZHDGPS", "1.08.190304.RG13GL.SG1396126309745", "1_00_150805_android_gpsdevice_SNH1940F0168678"}
)

func GetUserAgentRand() string {
	return ntrip_user_agents[rand.Intn(len(ntrip_user_agents))]
}

type NtripClient struct {
	TcpClient *dnet.ASyncTcpClientEx

	LogTypeStr          string
	RemoteAddress       string
	MountPoint          string
	TraceID             string
	User                string
	Pass                string
	state               int
	recvCnt             int
	authstr             string
	userAgent           string
	postHeader          []byte
	trimNtripShake      byte // 1: eventRecvBuffer 首次获取数据时截掉ICY认证信息, 默认为0不截取
	authflag            int8
	LastFirstRecv       string
	LastFirstRecvT      time.Time
	eventRecvBuffer     *EventNtripClientBuffer
	eventOnDisconnected *EventNtripClient
	eventOnConnected    *EventNtripClient
	OnDailErr           func(sender *NtripClient, err error)
	OnAuthSuccEvent     func(sender *NtripClient, msg string)
	OnAuthFailEvent     func(sender *NtripClient, msg string)
	OnSourceTableEvent  func(sender *NtripClient, sourcetable string)
}

func NewNtripClient() *NtripClient {
	rval := &NtripClient{
		TcpClient: dnet.NewASyncTcpClientEx(),
		userAgent: ntrip_user_agents[rand.Intn(len(ntrip_user_agents))],
	}
	rval.TcpClient.OnDisconnected = rval.onDisconnected
	rval.TcpClient.OnConnected = rval.onConnected
	rval.TcpClient.OnRecvBuffer = rval.onRecvBuffer
	rval.TcpClient.BeforeConnectCb = rval.onBeforeConnect
	rval.TcpClient.OnErrEvent = func(sender interface{}, err error, errtype int) {
		if errtype == dnet.ERR_TYPE_CONNECT {
			if rval.OnDailErr != nil {
				rval.OnDailErr(rval, err)
			}
		}

	}

	return rval
}

func (this *NtripClient) GetPostHeader() string {
	return string(this.postHeader)
}

func (this *NtripClient) StepStatus() string {
	if this.IsActive() {
		if this.state == 1 {
			if this.recvCnt > 1 {
				return fmt.Sprintf("收到数据(%.0f s)", time.Since(this.GetConnectedTime()).Seconds())
			}
			if this.authflag == 1 {
				return "认证成功"
			} else {
				return "认证未成功"
			}
		} else {
			return "已连接(尚未接收到数据)"
		}
	} else {
		return "未连接"
	}
}

/**
 * USER:PASS@IP:PORT
 */
func (this *NtripClient) SetConnectString(connString string) bool {
	var strAddr string = connString
	idx := strings.LastIndex(connString, "@")
	if idx != -1 {
		strAuth := connString[:idx]
		strs := strings.SplitN(strAuth, ":", 2)
		if len(strs) == 2 {
			this.SetNtripClientUser(strs[0], strs[1])
		} else {
			this.SetNtripClientUser(strs[0], "")
		}

		strAddr = connString[idx+1:]
	} else {
		this.SetNtripClientUser("", "")
	}

	this.SetRemoteAddress(strAddr)

	return true

}

func (this *NtripClient) GetRecvCnt() int {
	return this.recvCnt
}

func (this *NtripClient) IsActive() bool {
	return this.TcpClient.IsActive()
}

func (this *NtripClient) GetSourceTable(timeout time.Duration) (src string, reterr error) {
	str := "GET / HTTP/1.0\r\n"
	str += fmt.Sprintf("User-Agent:NTRIP %s\r\n", this.userAgent)
	if len(this.authstr) > 0 {
		str += "Authorization: Basic " + this.authstr
		str += "\r\n"
	}
	str += "\r\n"
	postBuf := []byte(str)
	var conn net.Conn
	var err error
	if this.TcpClient.DailCb != nil {
		conn, err = this.TcpClient.DailCb("tcp", this.RemoteAddress)
	}

	if err == nil && conn == nil {
		conn, err = net.DialTimeout("tcp", this.RemoteAddress, timeout)
	}

	if err != nil {
		reterr = err
		return
	}

	this.Reload()
	conn.Write(postBuf)
	buf := make([]byte, 1024)

	conn.SetReadDeadline(time.Now().Add(timeout))
	n, err := conn.Read(buf)
	if err != nil {
		reterr = err
		return
	}

	src = string(buf[:n])
	return

}

func (this *NtripClient) GetConnectedTime() time.Time {
	return this.TcpClient.GetConnectedTime()
}

func (this *NtripClient) GetStartTime() time.Time {
	return this.TcpClient.GetStartTime()
}

func (this *NtripClient) GetWorkTimeInfo() string {
	return this.TcpClient.GetWorkTimeInfo()
}

func (this *NtripClient) GetStopTime() time.Time {
	return this.TcpClient.GetStopTime()
}

func (this *NtripClient) LastDisreason() string {
	return this.TcpClient.LastDisMsg
}

func (this *NtripClient) RequestDisconnect(reason string) {
	this.Reload()
	this.TcpClient.RequestDisconnect(reason)
}

func (this *NtripClient) SetOnRecvBuffer(event EventNtripClientBuffer) {
	this.eventRecvBuffer = &event
}

func (this *NtripClient) SetOnConnected(event EventNtripClient) {
	this.eventOnConnected = &event
}

func (this *NtripClient) SetUserAgent(agent string) {
	this.userAgent = agent
}

func (this *NtripClient) SetOnDisconnected(event EventNtripClient) {
	this.eventOnDisconnected = &event
}

func (this *NtripClient) GetRemoteAddress() string {
	return this.TcpClient.GetRemoteAddress()
}

func (this *NtripClient) SetRemoteAddress(addr string) bool {
	if this.RemoteAddress != addr {
		this.RemoteAddress = addr
		return true
	} else {
		return false
	}
}

/*
*

	去掉握手信息
*/
func (this *NtripClient) SetTrimNtripShake(v byte) bool {
	if this.trimNtripShake != v {
		this.trimNtripShake = v
		return true
	} else {
		return false
	}
}

func (this *NtripClient) SetRequestMountPoint(val string) bool {
	if this.MountPoint != val {
		this.MountPoint = val
		return true
	}
	return false
}

func (this *NtripClient) SetNtripClientUser(user string, pass string) bool {
	this.User = user
	this.Pass = pass
	str := this.User + ":" + this.Pass
	strBase := base64.StdEncoding.EncodeToString([]byte(str))
	if this.authstr != strBase {
		this.authstr = strBase
		return true
	} else {
		return false
	}
}

func (this *NtripClient) onRecvBuffer(sender *dnet.ASyncTcpClientEx, buffer []byte) {
	// logger.Debug(this.MountPoint, ":",  this.RemoteAddress, "接受到数据:", len(buffer))
	this.recvCnt++
	if this.state == 0 {
		this.LastFirstRecv = string(buffer)
		this.LastFirstRecvT = time.Now()
		if len(this.LogTypeStr) > 0 {
			utils.Log.DebugTypef(this.LogTypeStr, "[From NtripCaster](%s):%s", this.String(), string(buffer))
		}

		this.state = 1

		firststr := string(buffer)

		if strings.Contains(firststr, "ICY 200 OK") {
			event := this.OnAuthSuccEvent
			if event != nil {
				event(this, firststr)
			}
			this.authflag = 1
			if this.trimNtripShake == 1 {
				buffer = utils.TrimPrefixBytes(buffer, []byte("ICY 200 OK")...)
				buffer = utils.TrimPrefixBytes(buffer, []byte("\r\n")...)
				buffer = utils.TrimPrefixBytes(buffer, []byte("\r\n")...)
				if len(buffer) == 0 {
					return
				}
			}
		} else if strings.Contains(firststr, "SOURCE TABLE") {
			event := this.OnSourceTableEvent
			if event != nil {
				event(this, firststr)
			}
			if this.trimNtripShake == 1 {
				return
			}
		} else {
			this.authflag = -1
			event := this.OnAuthFailEvent
			if event != nil {
				event(this, firststr)
			}
			if this.trimNtripShake == 1 {
				return
			}
		}

	}
	if this.eventRecvBuffer != nil {
		(*this.eventRecvBuffer)(this, buffer)
	}
}

func (this *NtripClient) CheckKickOut(isec int) {
	str := this.TcpClient.CheckRecvTimeOutReason(isec)
	if len(str) > 0 {
		this.TcpClient.RequestDisconnect(str)
	}
}

func (this *NtripClient) String() string {
	return fmt.Sprintf("[%d]%s:***@%s/%s", this.TcpClient.GetObjectID(), this.User, this.TcpClient.GetRemoteAddress(), this.MountPoint)
}

func (this *NtripClient) PostBuffer(buf []byte) {
	this.TcpClient.PostSendBuffer(buf)
}

func (this *NtripClient) onBeforeConnect(sender *dnet.ASyncTcpClientEx) interface{} {
	return true
}

func (this *NtripClient) onConnected(sender *dnet.ASyncTcpClientEx) {
	this.state = 0
	this.recvCnt = 0
	this.authflag = 0
	this.PostBuffer(this.postHeader)
	if len(this.LogTypeStr) > 0 {
		utils.Log.DebugTypef(this.LogTypeStr, "[%s]连接成功.\n%s", this.String(), string(this.postHeader))
	}
	if this.eventOnConnected != nil {
		(*this.eventOnConnected)(this)
	}
}

func (this *NtripClient) GetState() int {
	return this.state
}

func (this *NtripClient) ConnString() string {
	return fmt.Sprintf("%s:%s@%s/%s", this.User, this.Pass, this.RemoteAddress, this.MountPoint)
}

func (this *NtripClient) StatusString() string {
	str := ""
	if this.IsActive() {
		str = "active"
		if this.TcpClient.Connected() {
			str += fmt.Sprintf(",connected[%s]", utils.DateTimeString2(this.TcpClient.GetConnectedTime()))
		} else {
			str += fmt.Sprintf(",%s", this.LastDisreason())
		}
	} else {
		str = "stop"
	}
	return str
}

func (this *NtripClient) onDisconnected(sender *dnet.ASyncTcpClientEx) {
	if len(this.LogTypeStr) > 0 {
		utils.Log.DebugTypef(this.LogTypeStr, "[%s]连接断开!", this.String())
	}
	if this.eventOnDisconnected != nil {
		(*this.eventOnDisconnected)(this)
	}
}

// 重新加载配置
func (this *NtripClient) Reload() {
	str := "GET /" + this.MountPoint + " HTTP/1.0\r\n"
	str += fmt.Sprintf("User-Agent:NTRIP %s\r\n", this.userAgent)
	if len(this.TraceID) > 0 {
		str += fmt.Sprintf("TraceID:%s\r\n", this.TraceID)
	}
	if len(this.User) > 0 {
		str := this.User + ":" + this.Pass
		strBase := base64.StdEncoding.EncodeToString([]byte(str))
		this.authstr = strBase
	}
	if len(this.authstr) > 0 {
		str += "Authorization: Basic " + this.authstr
		str += "\r\n"
	}
	str += "\r\n"
	this.postHeader = []byte(str)
	this.TcpClient.SetAddress(this.RemoteAddress)
}

func (this *NtripClient) Open() {
	this.Reload()
	this.TcpClient.SetAddress(this.RemoteAddress)
	this.TcpClient.Start()
}

func (this *NtripClient) Close() {
	this.TcpClient.Stop()
	this.postHeader = nil
}
